#!/usr/bin/env python
#
#       This is a program for all ADC Module. It 
#   convert analog singnal to digital signal.
#
#       This program is most analog signal modules' 
#   dependency. Use it like this:
#       `import ADC0832`
#       `sig = ADC0832.getResult(chn)`
#
#   *'chn' should be 0 or 1 represent for ch0 or ch1
#   on ADC0832
#       
#         ACD1302                 Pi
#           CS ---------------- Pin 11
#           CLK --------------- Pin 12
#           DI ---------------- Pin 13

#           VCC ----------------- 3.3V
#           GND ------------------ GND
#

import RPi.GPIO as GPIO
import wiringpi
import time

# 定义GPIO引脚常量（默认使用BCM编号）
ADC_CS  = 17 #11  # 片选引脚（Chip Select）
ADC_CLK = 18 #12  # 时钟引脚（SPI Clock）
ADC_DIO = 27 #13  # 数据输入输出引脚（Data Input/Output）
usdelay = 2 # max CLK frequency is 400KHz, here set duty as 2*2us
# 时钟信号高低电平持续时间（微秒），最大支持400KHz时钟频率
T_convert = 8 * 2*usdelay # ADC convert time
# ADC转换总时间（8位转换 * 2边沿 * 单边延迟）

# 使用默认引脚保持向后兼容性
# using default pins for backwards compatibility
def setup(cs=17,clk=18,dio=27): #11,12,13
    global ADC_CS, ADC_CLK, ADC_DIO
    ADC_CS=cs  # 更新全局片选引脚
    ADC_CLK=clk  # 更新全局时钟引脚
    ADC_DIO=dio  # 更新全局数据引脚
    GPIO.setwarnings(False)  # 禁用GPIO警告
    GPIO.setmode(GPIO.BCM)   # Number GPIOs by its physical location
    # 使用BCM编号模式（区别于物理引脚编号）
    GPIO.setup(ADC_CS, GPIO.OUT)  # Set pins' mode is output
    # 配置片选引脚为输出模式
    GPIO.setup(ADC_CLK, GPIO.OUT)  # Set pins' mode is output
    # 配置时钟引脚为输出模式

def destroy():
    GPIO.cleanup()  # 清理GPIO设置，释放引脚资源

# using channel = 0 as default for backwards compatibility
# 使用通道0作为默认保持向后兼容性
def getResult(channel=0):  # Get ADC result, input channal
    # 获取指定通道的ADC转换结果
    # 初始化通信时序
    GPIO.setup(ADC_DIO, GPIO.OUT)  # 配置数据引脚为输出模式（发送模式）
    GPIO.output(ADC_CS, 0)  # 拉低片选信号，启动通信
    
    # 发送起始位和配置位
    GPIO.output(ADC_CLK, 0)  # 确保时钟初始为低电平
    GPIO.output(ADC_DIO, 1);  wiringpi.delayMicroseconds(usdelay)  # 起始位1
    
    GPIO.output(ADC_CLK, 1);  wiringpi.delayMicroseconds(usdelay)  # 上升沿触发
    
    GPIO.output(ADC_CLK, 0)  # 时钟回到低电平
    GPIO.output(ADC_DIO, 1);  wiringpi.delayMicroseconds(usdelay)  # 起始位2
    
    GPIO.output(ADC_CLK, 1);  wiringpi.delayMicroseconds(usdelay)  # 上升沿触发
    
    # 发送通道选择位
    GPIO.output(ADC_CLK, 0)  # 时钟低电平
    GPIO.output(ADC_DIO, channel);  wiringpi.delayMicroseconds(usdelay)  # 0=通道0，1=通道1
    
    GPIO.output(ADC_CLK, 1);  wiringpi.delayMicroseconds(usdelay)  # 上升沿触发通道选择
    
    # 准备读取数据
    GPIO.output(ADC_CLK, 0)  # 时钟回到低电平
    GPIO.setup(ADC_DIO, GPIO.IN);  wiringpi.delayMicroseconds(T_convert)  # 切换数据引脚为输入模式
    
    # 读取第一个字节（高位在前）
    dat1 = 0  # 初始化数据存储变量
    for i in range(0, 8):
        GPIO.output(ADC_CLK, 1);  wiringpi.delayMicroseconds(usdelay)  # 上升沿触发数据输出
        GPIO.output(ADC_CLK, 0);  wiringpi.delayMicroseconds(usdelay)  # 下降沿准备读取
        dat1 = dat1 << 1 | GPIO.input(ADC_DIO)  # 左移并合并新读取的位
    
    # 读取第二个字节（低位在前）
    dat2 = 0  # 初始化第二个数据存储变量
    for i in range(0, 8):
        dat2 = dat2 | GPIO.input(ADC_DIO) << i  # 按位合并数据（逆序）
        GPIO.output(ADC_CLK, 1);  wiringpi.delayMicroseconds(usdelay)  # 上升沿触发
        GPIO.output(ADC_CLK, 0);  wiringpi.delayMicroseconds(usdelay)  # 下降沿准备
    
    # 结束通信
    GPIO.output(ADC_CLK, 1)  # 时钟置高
    GPIO.output(ADC_CS, 1);  wiringpi.delayMicroseconds(usdelay)  # 拉高片选，结束通信
    GPIO.output(ADC_CLK, 0);  wiringpi.delayMicroseconds(usdelay)  # 时钟回到低电平
    
    # 校验两个字节数据一致性
    if dat1 == dat2:
        return dat1  # 返回有效数据
    else:
        return 0  # 数据不一致返回0

def loop():
    while True:
        res0 = getResult(0)  # 获取通道0数据
        res1 = getResult(1)  # 获取通道1数据
        print ('res0 = %d, res1 = %d' % (res0,res1))  # 打印双通道结果
        time.sleep(0.4)  # 400ms间隔

if __name__ == '__main__':      # 程序入口
    setup()  # 初始化GPIO配置
    try:
        loop()  # 进入主循环
    except KeyboardInterrupt:   # When 'Ctrl+C' is pressed, the child program destroy() will be  executed.
    # 捕获Ctrl+C信号
        destroy()  # 执行清理操作